////////////////////////////////////////////////////////////////////////////////
//
//  The MIT License
//
//  Copyright (c) 2008 - 2009. Dimarik
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////

package tx.framework.service {

	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.net.URLVariables;
	import flash.utils.ByteArray;

	import tx.framework.logging.Log;
	import tx.framework.logging.LogEventLevel;
	import tx.framework.utils.ObjectUtil;

	/**
	 *  @author				Dimarik
	 *  @version			1.0
	 *  @langversion		3.0
	 *  @playerversion		9.0
	 *
	 */
	public class HTTPService extends EventDispatcher {

		//---------------------------------------------------------------------
		//
		//   Constructor
		//
		//---------------------------------------------------------------------

		public function HTTPService() {
			super();

			this.loader.dataFormat = URLLoaderDataFormat.BINARY;
			this.loader.addEventListener(Event.COMPLETE, 					this.handler_success);
			this.loader.addEventListener(IOErrorEvent.IO_ERROR,				this.handler_fault);
			this.loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_fault);
			this.loader.addEventListener(HTTPStatusEvent.HTTP_STATUS,		this.handler_httpStatus);
		}

		//---------------------------------------------------------------------
		//
		//   Properties
		//
		//---------------------------------------------------------------------

		//-----------------------------
		//   url
		//-----------------------------
		private var _url:	String = '';

		public function get url():String {
			return this._url;
		}

		public function set url(value:String):void {
			if (this._url == value) return;
			this._url = value;
		}

		//-----------------------------
		//   method
		//-----------------------------
		private var _method:	String = URLRequestMethod.POST;

		public function get method():String {
			return this._method;
		}

		public function set method(value:String):void {
			if (this._method == value) return;
			this._method = value;
		}

		//-----------------------------
		//   dataFormat
		//-----------------------------
		private var _dataFormat:	String;

		public function get dataFormat():String {
			return this.loader.dataFormat;
		}

		public function set dataFormat(value:String):void {
			if (this.loader.dataFormat == value) return;
			this.loader.dataFormat = value;
		}

		//-----------------------------
		//   httpStatus
		//-----------------------------
		private var _httpStatus:	int;

		public function get httpStatus():int {
			return this._httpStatus;
		}

		//---------------------------------------------------------------------
		//
		//   Protected constants
		//
		//---------------------------------------------------------------------

		protected const loader:			URLLoader = new URLLoader();

		protected var currentRequest:	Object;

		//---------------------------------------------------------------------
		//
		//   Private constants
		//
		//---------------------------------------------------------------------

		private const _queue:			Array = new Array();

		//---------------------------------------------------------------------
		//
		//   Private variables
		//
		//---------------------------------------------------------------------

		//---------------------------------------------------------------------
		//
		//   Public methods
		//
		//---------------------------------------------------------------------

		//------------------------------------------
		// Common Remote Procedure Call
		//------------------------------------------
		public function call(apiMethod:String, responder:Object, options:Object = null):void {
			var requestObject:Object = new Object();
			requestObject.responder = responder;
			requestObject.method = apiMethod;
			requestObject.options = options;

			this._queue.push(requestObject);

			if( !this.currentRequest ) this.requestNext();
		}

		//------------------------------------------
		//  Load data
		//------------------------------------------
		public function load(responder:Object, options:Object = null):void {
			var requestObject:Object = new Object();
			requestObject.responder = responder;
			requestObject.method = '';
			requestObject.options = options;

			this._queue.push(requestObject);

			if( !this.currentRequest ) this.requestNext();
		}

		//------------------------------------------
		//  close channel
		//------------------------------------------	

		public function close():void {

			try {
				this.loader.close();
			} catch (error:Error) {
				trace('HTTPService # close error: ' + error.toString());
			}

			this._queue.splice(0, this._queue.length);
			this.currentRequest = null;
		}

		//---------------------------------------------------------------------
		//
		//   Protected methods
		//
		//---------------------------------------------------------------------

		/**
		 * @private
		 */
		protected function requestNext():void {
			if (this._queue.length == 0) return;

			this.currentRequest = this._queue.shift();

			var method:		String = this.currentRequest.method;
			var options:	Object = this.currentRequest.options;

			var params:Array = new Array();

			if (method) params['method'] = method;

			for (var i:String in options) {
				if (
					options[i] is String ||
					options[i] is Number ||
					options[i] is Boolean
				) {
					params[i] = options[i];
		    	} else {
		    		params[i] = this.convertToArray(options[i]);
		    	}
		    }

			var ba:ByteArray = new ByteArray();
		    ba.writeObject(params);

			// Debug info for server
			var vars:URLVariables = new URLVariables();
			if (params.hasOwnProperty('object')) vars['object'] = params.object;
			if (params.hasOwnProperty('action')) vars['action'] = params.action;

			if (params.hasOwnProperty('form')) {
				if (params.form.hasOwnProperty('code')) {
					vars['code'] = params.form.code;
				}
			}

			if (params.hasOwnProperty('viewer_id')) vars['viewer_id'] = params.viewer_id;
			

			var request:URLRequest = new URLRequest();
			request.url 		   = this._url + '?' + vars.toString();// + '?rnd=' + new Date().getTime();

			request.method 		   = this._method;
			request.data 		   = ba;

			var arr:Array = new Array(LogEventLevel.INFO, method + ' ' + request.url + ' ' + ObjectUtil.toString(params));
			Log.getLogger("DW-OUT").log.apply(null, arr);

			this.loader.load(request);
		}

		//---------------------------------------------------------------------
		//
		//   Private methods
		//
		//---------------------------------------------------------------------

		private function convertToURLVariables(value:Object, vars:URLVariables = null):URLVariables {
			if (!vars) vars = new URLVariables();

			for (var i:String in value) {

				if ( 
					!(value[i] is String) &&
					!(value[i] is Number) &&
					!(value[i] is Boolean)
				) value[i] = this.convertToURLVariables(value[i]);

		    	vars[i] = value[i];
			}

			return vars;
		}

		private function convertToArray(value:Object):Array {
			var result:Array = new Array();

			for (var i:String in value) {

				if ( 
					!(value[i] is String) &&
					!(value[i] is Number) &&
					!(value[i] is Boolean)
				) value[i] = this.convertToArray(value[i]);

		    	result[i] = value[i];
			}

			return result;
		}

		//---------------------------------------------------------------------
		//
		//   Event handlers
		//
		//---------------------------------------------------------------------

		/**
		 * @private
		 */
		protected function handler_success(event:Event):void {
			this.currentRequest.responder.result( (event.target as URLLoader).data );
			this.currentRequest = null;
			this.requestNext();
		}

		/**
		 * @private
		 */
		protected function handler_fault(event:Event):void {
			var loader:URLLoader = event.currentTarget as URLLoader;
			this.currentRequest.responder.fault(loader.data);
			this.currentRequest = null;
			this.requestNext();
		}

		/**
		 * @private
		 */
		protected function handler_httpStatus(event:HTTPStatusEvent):void {
			this._httpStatus = event.status;
			Log.getLogger("DW-OUT").log.apply(null, [LogEventLevel.INFO, 'HTTP_STATUS: ' + this._httpStatus]);
		}		


	}
}